﻿using iTool.SQL.Analysis.Context;
using iTool.SQL.Analysis.Context.Options;
using iTool.SQL.Analysis.SubQuery;

using Microsoft.Extensions.Logging;
using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iTool.SQL.Analysis
{
    public abstract class SqlAnalysisBase
    {
        protected SqlAnalysisContext context;
        protected SqlCodeObject CodeObject;
        protected SqlAnalysisBase Parent;
        protected WhereBase WhereOptions;
        protected object WhereProperty;


        public int Index { get; set; }

        public SqlAnalysisBase(SqlAnalysisContext context) 
        {
            this.context = context;
        }

        public abstract void Analysis();

        protected void AnalysisSubQuery()
        {
            if (this.CodeObject is SqlQuerySpecification)
            {
                context.ChildrenQuerys = context.ChildrenQuerys ?? new List<SqlSelectAnalysisContext>();
                context.ChildrenQuerys.Add(new SqlSelectAnalysisContext());
                var analysiser = new SqlQuerySpecificationAnalysis(context.ChildrenQuerys[^1]);
                analysiser.SetCodeObject(this.CodeObject);
                analysiser.Analysis();
                foreach (var item in context.ChildrenQuerys[^1].Errors)
                {
                    Console.WriteLine(item);
                }
            }


            //if (this.context.ChildrenQuerys?.Any() == true)
            //{
            //    for (int i = 0; i < context.ChildrenQuerys.Count; i++)
            //    {
            //        context.ChildrenQueryTables.Add(new SqlSelectAnalysisContext()); 
            //        SqlQuerySpecification? subQuery = this.context.ChildrenQuerys[i];
            //        var analysiser = new SqlQuerySpecificationAnalysis(context.ChildrenQueryTables[^1]);
            //        analysiser.SetCodeObject(subQuery);
            //        analysiser.Analysis();
            //        context.ChildrenQueryTables = null;
            //        foreach (var item in context.ChildrenQueryTables[^1].Errors)
            //        {
            //            Console.WriteLine(item);
            //        }
            //    }
            //    context.ChildrenQuerys = null;
            //}
        }
        public SqlAnalysisContext GetSqlAnalysisContext() => this.context;

        protected virtual void Next(
            Func<int, SqlCodeObject, SqlAnalysisBase, bool>? beforeAction = null, 
            Action<int, SqlCodeObject, SqlAnalysisBase>? afterAction = null,
            WhereBase whereOptions = null,
            object whereProperty = null)
        {
            if (this.CodeObject?.Children != null)
            {
                int index = 0;
                foreach (var item in this.CodeObject.Children)
                {
                    SqlAnalysisBase sqlAnalysis = SqlAnalysisFactory.GetNextSqlAnalysis(item.GetType(), this.context);
                    if (sqlAnalysis?.Validation(this.CodeObject) == true)
                    {
                        if (beforeAction == null || beforeAction?.Invoke(index, item, sqlAnalysis) == true)
                        {
                            sqlAnalysis.Parent = this;
                            sqlAnalysis.Index = index;
                            sqlAnalysis.SetCodeObject(item);
                            if (sqlAnalysis.WhereOptions == null) 
                            { 
                                sqlAnalysis.SetWhereOptions(whereOptions);
                            }
                            sqlAnalysis.SetWhereOptions(whereProperty:whereProperty);
                            sqlAnalysis.Analysis();
                        }
                        afterAction?.Invoke(index,item, sqlAnalysis);
                    }
                    index++;
                }
            }
        }

        private void SetCodeObject(SqlCodeObject codeObject)
        {
            this.CodeObject = codeObject;
        }

        public void SetWhereOptions(WhereBase whereOptions = null, object whereProperty = null)
        {
            this.WhereOptions = whereOptions ?? this.WhereOptions;
            this.WhereProperty = whereProperty ?? this.WhereProperty;
        }

        protected bool Validation<Tin>(SqlCodeObject codeObject)
            where Tin : SqlCodeObject
        {
            this.CodeObject = codeObject.Children.OfType<Tin>().FirstOrDefault();
            return this.CodeObject != null;
        }

        protected void DebugLogger<T>(string message)
        {
        }
        protected void DebugLogger<T>(Exception ex)
        {
        }

        public abstract bool Validation(SqlCodeObject codeObject);


        protected List<GroupWhereOptions> GetGroupWhere()
        {

            // GroupBy
            // QueryTables
            // WhereBuilder

            return this.IsHaving() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].GroupBy.GroupWheres
                        : (this.IsJoin() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].GroupWheres : this.context.GroupWheres);

            //return this.IsJoin() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].WhereBuilder
            //            : (this.IsHaving() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].GroupBy.WhereBuilder : this.context.WhereBuilder);
        }

        //protected StringBuilder GetWhereBuilder() 
        //{

        //    // GroupBy
        //    // QueryTables
        //    // WhereBuilder

        //    return this.IsHaving() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].GroupBy.WhereBuilder
        //                : (this.IsJoin() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].WhereBuilder : this.context.WhereBuilder);

        //    //return this.IsJoin() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].WhereBuilder
        //    //            : (this.IsHaving() ? ((SqlSelectAnalysisContext)this.context).QueryTables[^1].GroupBy.WhereBuilder : this.context.WhereBuilder);
        //}

        protected bool IsJoin()
        {
            //return this.context is SqlSelectAnalysisContext;


            return this.CodeObject.Parent is SqlQualifiedJoinTableExpression
                    || this.CodeObject.Parent.Parent is SqlQualifiedJoinTableExpression
                    || this.CodeObject.Parent.Parent?.Parent is SqlQualifiedJoinTableExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent is SqlQualifiedJoinTableExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent?.Parent is SqlQualifiedJoinTableExpression
                ||
                this.CodeObject.Parent is SqlBinaryQueryExpression
                    || this.CodeObject.Parent.Parent is SqlBinaryQueryExpression
                    || this.CodeObject.Parent.Parent?.Parent is SqlBinaryQueryExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent is SqlBinaryQueryExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent?.Parent is SqlBinaryQueryExpression
                ||
                this.CodeObject.Parent is SqlDerivedTableExpression
                    || this.CodeObject.Parent.Parent is SqlDerivedTableExpression
                    || this.CodeObject.Parent.Parent?.Parent is SqlDerivedTableExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent is SqlDerivedTableExpression
                    || this.CodeObject.Parent.Parent?.Parent?.Parent?.Parent is SqlDerivedTableExpression;
        }

        protected bool IsHaving()
        {
            return this.CodeObject.Parent is SqlHavingClause
                        || this.CodeObject.Parent.Parent is SqlHavingClause
                        || this.CodeObject.Parent.Parent?.Parent is SqlHavingClause
                        || this.CodeObject.Parent.Parent?.Parent?.Parent is SqlHavingClause
                        || this.CodeObject.Parent.Parent?.Parent?.Parent?.Parent is SqlHavingClause;
        }
    }
}
